﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "GbmpClipBoardCmd.h"

#include "GbmpUiPlatformCommandRegister.h"
#include "GbmpUiPlatformCommandIds.h"
#include "ContainerUtil.h"
#include "ISelection.h"
#include "GbmpCopyActionBehavior.h"
#include "CmdStatusUtils.h"
#include "INotificationManager.h"
#include "IFamily.h"
#include "FamilyLoadOptionsWithUI.h"
#include "FamilyUtils.h"
#include "IUiDocumentViewManager.h"


#include "IEditMode.h"
#include "GbmpModelViewUtil.h"
#include "UiCommonDialog.h"
#include "IElementBasicInformation.h"
#include "IModelView.h"
#include "GbmpPasteAction.h"
#include "ElementId.h"
#include "IGraphicsNodeReference.h"
#include "LevelUtils.h"
#include "IUserTransaction.h"
#include "IUiDocument.h"
#include "IRegenerator.h"
#include "IDocument.h"
#include "../CopyPaste/ClipBoard.h"
#include "GbmpCopyToLevelDlg.h"
#include "UiDocumentViewUtils.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"


using namespace std;
using namespace gcmp;

REGISTER_GM_COMMAND(GbmpClipBoardCutCmd);
REGISTER_GM_COMMAND(GbmpClipBoardCopyCmd);
REGISTER_GM_COMMAND(GbmpClipBoardPasteCmd);
REGISTER_GM_COMMAND(GbmpClipBoardPasteToLevelCmd);

//////////////////////////////////////////////////////////////////////////
GbmpClipBoardCutCmd::GbmpClipBoardCutCmd() : GbmpCommandBase(ID_CMD_GBMP_CLIPBOARD_CUT)
{
}

bool GbmpClipBoardCutCmd::IsVisible() const
{ 
    CMD_ONLY_VISIBLE_IN_PROJECT_ENVIRONMENT
}

bool GbmpClipBoardCutCmd::IsEnabled() const
{
    if (!CommandBase::IsEnabled())
        return false;

    if (ISelection::Get()->IsEmpty())
        return false;

    CMD_ONLY_ENABLE_IN_PROJECT_ENVIRONMENT
}

//////////////////////////////////////////////////////////////////////////
GbmpClipBoardCopyCmd::GbmpClipBoardCopyCmd() : GbmpCommandBase(ID_CMD_GBMP_CLIPBOARD_COPY)
{
}

bool GbmpClipBoardCopyCmd::IsVisible() const
{ 
    CMD_ONLY_VISIBLE_IN_PROJECT_ENVIRONMENT
}

bool GbmpClipBoardCopyCmd::IsEnabled() const
{
    if (!CommandBase::IsEnabled())
        return false;

    if (ISelection::Get()->IsEmpty())
        return false;

    CMD_ONLY_ENABLE_IN_PROJECT_ENVIRONMENT
}

OwnerPtr<IAction> GbmpClipBoardCopyCmd::ExecuteCommand(const gcmp::CommandParameters& cmdParams)
{
    IDocument* pDoc = UiDocumentViewUtils::GetCurrentDocument();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");

    IModelView* pModelView = UiDocumentViewUtils::GetCurrentModelView();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");

    if (!(pModelView && GbmpModelViewUtil::IsPlanOrCeilingView(pModelView)))
    {
        UiCommonDialog::ShowMessageBox(GBMP_TR(L"复制不成功"),
            GBMP_TR(L"其他非平面视图暂不支持复制操作！"),
            (int)UiCommonDialog::ButtonType::OK);
        return nullptr;
    }

    if (!IsEnabled())
    {
        return nullptr;
    }

    //=========================================================================================
    //STEP1: 准备复制工作
    const auto& selectedElems = ISelection::Get()->GetGraphicsNodeReferences();
    vector<ElementId> selectedModelIds;
    FOR_EACH(elem, selectedElems)
    {
        selectedModelIds.push_back(elem->GetElementId());
    }

    //=========================================================================================
    //STEP2: 执行复制工作
    ClipBoard * pClipBoard = ClipBoard::Get();
    DBG_WARN_UNLESS(pClipBoard, L"获取默认的粘贴板管理器失败",L"GDMPLab",L"2024-03-30");
    IElementBasicInformation* pElementInfo = pModelView->GetBasicInformation();
    bool ret = pClipBoard->Copy(pDoc, pElementInfo->GetElementId(), selectedModelIds);
    if (!ret)
    {
        UiCommonDialog::ShowMessageBox(GBMP_TR(L"复制"), GBMP_TR(L"复制不成功！"), (int)UiCommonDialog::ButtonType::Yes);
    }

    return nullptr;
}

//////////////////////////////////////////////////////////////////////////
GbmpClipBoardPasteCmd::GbmpClipBoardPasteCmd() : GbmpCommandBase(ID_CMD_GBMP_CLIPBOARD_PASTE)
{
}

bool GbmpClipBoardPasteCmd::IsVisible() const 
{ 
    CMD_ONLY_VISIBLE_IN_PROJECT_ENVIRONMENT
}

bool GbmpClipBoardPasteCmd::IsEnabled() const
{
    if (!CommandBase::IsEnabled())
        return false;

    ClipBoard * pClipBoard = ClipBoard::Get();
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pClipBoard, L"获取粘贴板管理器失败",L"GDMPLab",L"2024-03-30");

    if(!pClipBoard->HasContent())
        return false;

    CMD_ONLY_ENABLE_IN_PROJECT_ENVIRONMENT
}

OwnerPtr<IAction> GbmpClipBoardPasteCmd::ExecuteCommand(const gcmp::CommandParameters& cmdParams)
{
    if (!IsEnabled())
    {
        return nullptr;
    }

    IDocument* pDoc = UiDocumentViewUtils::GetCurrentDocument();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");

    IModelView* pModelView = UiDocumentViewUtils::GetCurrentModelView();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");

    //启动粘贴ACTION:
    //获得平面上的一个坐标点，并进行粘贴
    //目前，非平面粘贴也从这个入口进入，后续可能需要独立出来
    return NEW_AS_OWNER_PTR(GbmpPasteAction);
}

//////////////////////////////////////////////////////////////////////////
GbmpClipBoardPasteToLevelCmd::GbmpClipBoardPasteToLevelCmd() : GbmpCommandBase(ID_CMD_GBMP_CLIPBOARD_PASTE_TO_LEVEL)
{
}

bool GbmpClipBoardPasteToLevelCmd::IsVisible() const 
{
    CMD_ONLY_VISIBLE_IN_PROJECT_ENVIRONMENT
}

bool GbmpClipBoardPasteToLevelCmd::IsEnabled() const
{
    if (!CommandBase::IsEnabled())
        return false;

    ClipBoard * pClipBoard = ClipBoard::Get();
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pClipBoard, L"获取粘贴板管理器失败",L"GDMPLab",L"2024-03-30");


    if(!pClipBoard->HasContent())
        return false;

    CMD_ONLY_ENABLE_IN_PROJECT_ENVIRONMENT
}

namespace
{
    //临时解决GmModel依赖于GmModelFamily的问题
    //(TODO: 临时方案，后续会改进得更加合理）
    IFamily* LoadFamily_Temp(IDocument* pDoc, const std::wstring& filePathName)
    {
        std::wstring errorMsg;
        FamilyLoadOptionsWithUI familyLoadOptionsImpl;
        return quick_cast<IFamily>(gcmp::FamilyUtils::LoadFamily(pDoc, filePathName, errorMsg, &familyLoadOptionsImpl));
    }
}

OwnerPtr<IAction> GbmpClipBoardPasteToLevelCmd::ExecuteCommand(const gcmp::CommandParameters& cmdParams)
{
    IUiDocumentViewManager* pUiDocViewManager = IUiDocumentViewManager::Get();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pUiDocViewManager, L"无效的pUiDocViewManager指针",L"GDMPLab",L"2024-03-30");

    IUiDocument* pUIDoc = pUiDocViewManager->GetCurrentUiDocument();
    DBG_WARN_AND_RETURN_UNLESS(pUIDoc, nullptr, L"pUIDoc为空",L"GDMPLab",L"2024-03-30");

    IDocument* pDoc = UiDocumentViewUtils::GetCurrentDocument();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");
    IDocument* pCurrentDoc = pDoc;

    IModelView* pModelView = UiDocumentViewUtils::GetCurrentModelView();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");

    ClipBoard* pClipBoard = ClipBoard::Get();
    DBG_WARN_UNLESS(pClipBoard, L"获取默认的粘贴板管理器失败",L"GDMPLab",L"2024-03-30");
    DBG_WARN_AND_RETURN_UNLESS(pClipBoard, nullptr, L"pClipBoard为空!",L"GDMPLab",L"2024-03-30");
    
    const IDocument* pDocumentFrom = pClipBoard->GetFromDocument();
    DBG_WARN_AND_RETURN_UNLESS(pDocumentFrom, nullptr, L"pDocumentFrom为空!",L"GDMPLab",L"2024-03-30");

    ElementId levelId = pClipBoard->GetFromLevelId();
    ILevel* pLevelFrom = LevelUtils::GetLevelById(pDocumentFrom, levelId);
    DBG_WARN_AND_RETURN_UNLESS(pLevelFrom, nullptr, L"pLevelFrom为空!",L"GDMPLab",L"2024-03-30");

    GbmpCopyToLevelDlg copyToLevelDlg(pCurrentDoc, pCurrentDoc == pDocumentFrom ? pLevelFrom : nullptr);//这里未处理正确的标高
    if (QDialog::Accepted != copyToLevelDlg.exec())
    {
        return nullptr;
    }
    std::set<std::wstring> levelNames = copyToLevelDlg.GetExchangeData().selectedLevels;

    //启动事务(一次粘贴操作为一个事务单元)
    OwnerPtr<IUserTransaction> opUserTransaction = IUserTransaction::Create(pCurrentDoc, GBMP_TR(L"粘贴到标高"));
    DBG_WARN_AND_RETURN_UNLESS(opUserTransaction != nullptr, nullptr, L"事务创建不成功",L"GDMPLab",L"2024-03-30");

    vector<ElementId> levelsTo;
    FOR_EACH(levelName, levelNames)
    {
        levelsTo.push_back(LevelUtils::GetLevelIdByName(pCurrentDoc, levelName));
    }
    pClipBoard->SetLoadFamilyCallBack(LoadFamily_Temp);
    pClipBoard->SetModelViewId(pModelView->GetBasicInformation()->GetElementId());
    Vector3d defaultPt = pClipBoard->GetMinPt();
    std::vector<ElementId> pastedElementIds;
    pClipBoard->SetPasteToLevelIds(levelsTo);
    bool ret = pClipBoard->Paste(pCurrentDoc, pModelView->GetBasicInformation()->GetElementId(), defaultPt, pastedElementIds);
    if (!ret)
    {
        UiCommonDialog::ShowMessageBox(GBMP_TR(L"粘贴"), GBMP_TR(L"粘贴不成功，参考信息: \n") + pClipBoard->GetLastErrorMessage(), (int)UiCommonDialog::ButtonType::Yes);
        opUserTransaction->Rollback();
        pUIDoc->UpdateView();
    }
    else
    {
        GbmpPasteAction::UpdateStoreyLevel(pDoc, levelsTo, pastedElementIds);

        //新文档里只是生成了很多Element对象,需要关联更新一下
        pCurrentDoc->GetRegenerator()->RegenerateDocument();
        opUserTransaction->Commit();
        pUIDoc->UpdateView();

        UiCommonDialog::ShowMessageBox(GBMP_TR(L"粘贴"), GBMP_TR(L"粘贴成功！"), (int)UiCommonDialog::ButtonType::Yes);
    }

    return nullptr;
}
